/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'
import { BehaviorSubject, Observable, debounceTime, distinctUntilChanged, first, tap, flatMap, map } from 'rxjs'
import semverGt from 'semver/functions/gt'

import { Component, HostBinding, Input } from '@angular/core'
import { ConfigService, PlatformService, PluginInfo } from 'tabby-core'
import { PluginManagerService } from '../services/pluginManager.service'

enum BusyState { Installing = 'Installing', Uninstalling = 'Uninstalling' }

const FORCE_ENABLE = ['tabby-core', 'tabby-settings', 'tabby-electron', 'tabby-web', 'tabby-plugin-manager']

_('Search plugins')

/** @hidden */
// 装饰器，用于定义组件的元数据
@Component({
    templateUrl: './pluginsSettingsTab.component.pug', // 组件模板路径
    styleUrls: ['./pluginsSettingsTab.component.scss'], // 组件样式路径
})
export class PluginsSettingsTabComponent {
    BusyState = BusyState // 定义忙碌状态枚举
    @Input() availablePlugins$: Observable<PluginInfo[]> // 可用插件的可观察对象
    @Input() availablePluginsQuery$ = new BehaviorSubject<string>('') // 可用插件查询的行为主体
    @Input() availablePluginsReady = false // 可用插件是否准备好的标志
    @Input() installedPluginsQuery$ = new BehaviorSubject<string>('') // 已安装插件查询的行为主体
    @Input() knownUpgrades: Record<string, PluginInfo|null> = {} // 已知升级插件的记录
    @Input() busy = new Map<string, BusyState>() // 忙碌状态的映射
    @Input() erroredPlugin: string // 出错的插件名称
    @Input() errorMessage: string // 错误信息

    @HostBinding('class.content-box') true // 绑定组件的CSS类

    installedPlugins$: PluginInfo[] = [] // 已安装插件的数组
    installedFilter = '' // 已安装插件的过滤条件
    availableFilter = '' // 可用插件的过滤条件

    constructor (
        private config: ConfigService, // 配置服务
        private platform: PlatformService, // 平台服务
        public pluginManager: PluginManagerService, // 插件管理服务
    ) {}

    ngOnInit () {
        // 初始化可用插件的可观察对象
        this.availablePlugins$ = this.availablePluginsQuery$
            .asObservable()
            .pipe(
                debounceTime(200), // 防抖动，延迟200毫秒
                distinctUntilChanged(), // 只有当值改变时才触发
                flatMap(query => {
                    this.availablePluginsReady = false // 设置可用插件未准备好
                    return this.pluginManager.listAvailable(query).pipe(tap(() => {
                        this.availablePluginsReady = true // 设置可用插件准备好
                    }))
                }),
            )

        // 订阅可用插件，并确定已知的升级
        this.availablePlugins$.pipe(first(), map((plugins: PluginInfo[]) => {
            plugins.sort((a, b) => a.name > b.name ? 1 : -1) // 按名称排序插件
            return plugins
        })).subscribe(available => {
            // 遍历已安装的插件，查找可用的升级
            for (const plugin of this.pluginManager.installedPlugins) {
                this.knownUpgrades[plugin.name] = available.find(x => x.name === plugin.name && semverGt(x.version, plugin.version)) ?? null
            }
        })

        // 初始化已安装插件的可观察对象
        this.installedPluginsQuery$
            .asObservable()
            .pipe(
                debounceTime(200), // 防抖动，延迟200毫秒
                distinctUntilChanged(), // 只有当值改变时才触发
                flatMap(query => {
                    return this.pluginManager.listInstalled(query) // 列出已安装的插件
                }),
            ).subscribe(plugin => {
                this.installedPlugins$ = plugin // 更新已安装插件的数组
            })
    }

    // 打开插件文件夹
    openPluginsFolder (): void {
        this.platform.openPath(this.pluginManager.userPluginsPath)
    }

    // 搜索可用插件
    searchAvailable (query: string) {
        this.availablePluginsQuery$.next(query)
    }

    // 搜索已安装插件
    searchInstalled (query: string) {
        this.installedPluginsQuery$.next(query)
    }

    // 检查插件是否已安装
    isAlreadyInstalled (plugin: PluginInfo): boolean {
        return this.pluginManager.installedPlugins.some(x => x.name === plugin.name)
    }


    // 异步安装插件方法
    async installPlugin (plugin: PluginInfo): Promise<void> {
        this.busy.set(plugin.name, BusyState.Installing) // 设置插件的忙碌状态为安装中
        try {
            await this.pluginManager.installPlugin(plugin) // 调用插件管理器的安装方法
            this.busy.delete(plugin.name) // 安装完成后，移除忙碌状态
            this.config.requestRestart() // 请求重启应用
        } catch (err) {
            this.erroredPlugin = plugin.name // 记录出错的插件名称
            this.errorMessage = err // 记录错误信息
            this.busy.delete(plugin.name) // 移除忙碌状态
            throw err // 抛出错误，以便上层调用者处理
        }
    }

    // 异步卸载插件方法
    async uninstallPlugin (plugin: PluginInfo): Promise<void> {
        this.busy.set(plugin.name, BusyState.Uninstalling) // 设置插件的忙碌状态为卸载中
        try {
            await this.pluginManager.uninstallPlugin(plugin) // 调用插件管理器的卸载方法
            this.busy.delete(plugin.name) // 卸载完成后，移除忙碌状态
            this.config.requestRestart() // 请求重启应用
        } catch (err) {
            this.erroredPlugin = plugin.name // 记录出错的插件名称
            this.errorMessage = err // 记录错误信息
            this.busy.delete(plugin.name) // 移除忙碌状态
            throw err // 抛出错误，以便上层调用者处理
        }
    }

    // 异步升级插件方法
    async upgradePlugin (plugin: PluginInfo): Promise<void> {
        return this.installPlugin(this.knownUpgrades[plugin.name]!) // 调用安装方法安装已知升级的插件
    }

    // 显示插件信息
    showPluginInfo (plugin: PluginInfo) {
        this.platform.openExternal('https://www.npmjs.com/package/' + plugin.packageName) // 打开插件的npm页面
    }

    // 显示插件主页
    showPluginHomepage (plugin: PluginInfo) {
        this.platform.openExternal(plugin.homepage ?? '') // 打开插件的主页链接
    }

    // 检查插件是否启用
    isPluginEnabled (plugin: PluginInfo) {
        return !this.config.store.pluginBlacklist.includes(plugin.name) // 检查插件是否在黑名单中
    }

    // 检查插件是否可以禁用
    canDisablePlugin (plugin: PluginInfo) {
        return !FORCE_ENABLE.includes(plugin.packageName) // 检查插件是否在强制启用列表中
    }

    togglePlugin (plugin: PluginInfo) {
        if (this.isPluginEnabled(plugin)) {
            this.disablePlugin(plugin)
        } else {
            this.enablePlugin(plugin)
        }
    }

    enablePlugin (plugin: PluginInfo) {
        this.config.store.pluginBlacklist = this.config.store.pluginBlacklist.filter(x => x !== plugin.name)
        this.config.save()
        this.config.requestRestart()
    }

    disablePlugin (plugin: PluginInfo) {
        this.config.store.pluginBlacklist = [...this.config.store.pluginBlacklist, plugin.name]
        this.config.save()
        this.config.requestRestart()
    }
}
